#!/usr/bin/env python
################################################################################
#            __  __            _   ____        _ _ ____        _              #
#           |  \/  | ___  __ _| |_| __ )  __ _| | | __ )  ___ | |_           #
#           | |\/| |/ _ \/ _` | __|  _ \ / _` | | |  _ \ / _ \| __|         #
#           | |  | |  __/ (_| | |_| |_) | (_| | | | |_) | (_) | |_          #
#           |_|  |_|\___|\__,_|\__|____/ \__,_|_|_|____/ \___/ \__|          #
#                                                                             #
#                                     by                                       #
# _______ .______       _______  _______   ______   ______    _______   _______ #
#|   ____||   _  \     |   ____||   ____| /      | /  __  \  |       \ |   ____| #
#|  |__   |  |_)  |    |  |__   |  |__   |  ,----'|  |  |  | |  .--.  ||  |__     #
#|   __|  |      /     |   __|  |   __|  |  |     |  |  |  | |  |  |  ||   __|     #
#|  |     |  |\  \----.|  |____ |  |____ |  `----.|  `--'  | |  '--'  ||  |____     #
#|__|     | _| `._____||_______||_______| \______| \______/  |_______/ |_______|   #
#                                                                                 #
#                              Uno Game Module                                   #
#                             Modular FrameWork                                 #
#
# Coders
#
#	Justin Giorgi (rebel_kid)
#
# Debuggers
#
#	Justin Giorgi (rebel_kid)
#	Lachlan Gilmour (chilli0)
#	David Liedle (spiderbyte)
#	(Polarina)
#	(prologic)
#
################################################################################
#                                                                              #
#                               GPL Licensed                                   #
#                                                                              #
################################################################################

# Imports
import random

class uno:

	# Sets the deck of cards
	globcards = ['b0', 'b1', 'b2', 'b3', 'b4', 'b5', 'b6','b7', 'b8', 'b9','b1', 'b2', 'b3', 'b4', 'b5', 'b6','b7', 'b8', 'b9','r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6','r7', 'r8', 'r9','r1', 'r2', 'r3', 'r4', 'r5', 'r6','r7', 'r8', 'r9','g0', 'g1', 'g2', 'g3', 'g4', 'g5', 'g6','g7', 'g8', 'g9','g1', 'g2', 'g3', 'g4', 'g5', 'g6','g7', 'g8', 'g9','y0', 'y1', 'y2', 'y3', 'y4', 'y5', 'y6','y7', 'y8', 'y9','y1', 'y2', 'y3', 'y4', 'y5', 'y6','y7', 'y8', 'y9', 'bd2', 'bd2', 'yd2', 'yd2', 'gd2', 'gd2', 'rd2', 'rd2', 'br', 'br', 'yr', 'yr', 'gr', 'gr', 'rr', 'rr', 'bs', 'bs', 'ys', 'ys', 'gs', 'gs', 'rs', 'rs', 'w', 'w', 'w', 'w', 'wd4', 'wd4', 'wd4', 'wd4']
	
	# Finds the word value of cards ie "b0" is "Blue 0"
	cardvalues = {'b0':'Blue 0', 'b1':'Blue 1', 'b2':'Blue 2', 'b3':'Blue 3', 'b4':'Blue 4', 'b5':'Blue 5', 'b6':'Blue 6','b7':'Blue 7', 'b8':'Blue 8', 'b9':'Blue 9', 'r0':'Red 0', 'r1':'Red 1', 'r2':'Red 2', 'r3':'Red 3', 'r4':'Red 4', 'r5':'Red 5', 'r6':'Red 6','r7':'Red 7', 'r8':'Red 8', 'r9':'Red 9' ,'y0':'Yellow 0', 'y1':'Yellow 1', 'y2':'Yellow 2', 'y3':'Yellow 3', 'y4':'Yellow 4', 'y5':'Yellow 5', 'y6':'Yellow 6','y7':'Yellow 7', 'y8':'Yellow 8', 'y9':'Yellow 9', 'g0':'Green 0', 'g1':'Green 1', 'g2':'Green 2', 'g3':'Green 3', 'g4':'Green 4', 'g5':'Green 5', 'g6':'Green 6','g7':'Green 7', 'g8':'Green 8', 'g9':'Green 9', 'bd2':'Blue Draw 2', 'yd2':'Yellow Draw 2', 'gd2':'Green Draw 2', 'rd2':'Red Draw 2', 'br':'Blue Reverse', 'yr':'Yellow Reverse', 'gr':'Green Reverse', 'rr':'Red Reverse', 'bs':'Blue Skip', 'ys':'Yellow Skip', 'gs':'Green Skip', 'rs':'Red Skip', 'w':'Wild', 'wd4':'Wild Draw Four'}

	# Starts the game
	def __init__(self, globalvars, msgvars):
	
		# Checks if a game is being played and sets startup variables
		if msgvars["chanid"] not in globalvars["localsometimes"].keys():
		
			# Checks if games are enabled
			if msgvars["features"][5]:
				self.cards = self.globcards
				self.globalvars = globalvars
				self.msgvars = msgvars
				self.playerscard = {}
				self.players = []
				random.shuffle(self.cards)
				self.wild = False
				globalvars["localsometimes"][msgvars["chanid"]] = self
				self.currentplayer = 0
				self.started = False
				self.drew = False
				self.stop = False
				self.lastcard = 'UNO'
				self.lastcolor = 'UNO'
				self._message(msgvars["channel"], "Welcome to UNO!")
			
			# Replies feature disabled
			else:
				msgvars["server"].privmsg(destination, "Sorry but that feature is disabled here.")
		
		# Replies game already playing
		else:
			msgvars["server"].privmsg(msgvars["destination"], 'A game is already started for this channel. Please type "stop" to stop the game.')
	
	# Handles messages to the game	
	def message(self, globalvars, msgvars):
		self.globalvars = globalvars
		self.msgvars = msgvars
		if msgvars["command"][0] == 'start':
			self._start(msgvars["channel"])
		elif msgvars["command"][0].startswith('jo'):
			self._join(msgvars["source"], msgvars["channel"])
		elif msgvars["command"][0].startswith('pl'):
			if msgvars["source"] == self.players[self.currentplayer]:
				self._userplay(' '.join(msgvars["command"][1:]).lower(), msgvars["source"], msgvars["channel"])
			else:
				self._notice(msgvars["source"], "It is not your turn.")
		elif msgvars["command"][0].startswith('co') and self.wild:
			if msgvars["source"] == self.wilder:
				self._wildcontinue(msgvars["source"], msgvars["channel"], ' '.join(msgvars["command"][1:]).lower())
			else:
				self._notice(msgvars["source"], "%s must choose the color." % (self.wilder))
		elif msgvars["command"][0].startswith('dr'):
			if msgvars["source"] == self.players[self.currentplayer]:
				self._draw(msgvars["source"], msgvars["channel"])
			else:
				self._notice(msgvars["source"], "It is not your turn.")
		elif msgvars["command"][0].startswith('pa'):
			if msgvars["source"] == self.players[self.currentplayer]:
				self._pass(msgvars["source"], msgvars["channel"])
			else:
				self._notice(msgvars["source"], "It is not your turn.")
		elif msgvars["command"][0].startswith('stop'):
			self._stop(msgvars["source"], msgvars["channel"])
	
	# Deals the first card of the game
	def _start(self, channel):
		if not self.started:
			card = self.cards.pop()
			cocard = self._findcard(card)
			color = cocard[0]
			card = cocard[1]
			if color == 'w' or card == 'r' or card == 's' or card == 'd2':
				self.currentplayer = 0
				self._start(channel)
			else:	
				self._message(channel, "Top card is %s, play starts with %s." % (self.cardvalues[color + card], self.players[self.currentplayer]))
				self.lastcolor = color
				self.lastcard = card
				self.started = True
		else:
			self._message(channel, "Uno has already been started.")
	
	# Adds a player to the game		
	def _join(self, source, channel):
		if source not in self.players:
			self.playerscard[source] = []
			self._dealcard(source, 7)
			self.players.append(source)
			self._message(channel, "%s joins uno!" % (source))
	
	# Handles players playing cards	
	def _userplay(self, message, source, channel):
		card = None
		color = None
		cocard = self._findcard(message)
		color = cocard[0]
		card = cocard[1]
		uno = cocard[2]
		if card is not None and color is not None:
			print self.playerscard[source]
			print color + card
			if color + card in self.playerscard[source]:
				if len(self.playerscard[source]) - 1 == 0:
					if color == self.lastcolor or card == self.lastcard and color != 'w':
						self._message(channel, "%s plays %s and wins UNO!" % (source, self.cardvalues[color + card]))
						self._win(channel, source)
					elif color == 'w':
						self._message(channel, "%s plays %s and wins UNO!" % (source, self.cardvalues[color + card]))
						self._win(channel, source)
				if color == self.lastcolor or card == self.lastcard and color != 'w':
					if card == 'd2':
						self._drawtwo(source, card, color, channel)
					elif card == 's':
						self._skip(source, card, color, channel)
					elif card == 'r':
						self._reverse(source, card, color, channel)
					else:	
						self._play(source, card, color, channel)
				elif color == 'w':
					self._wildstart(source, card, color, channel)
				else:
					self._notice(source, "You can't play that card.")
				if len(self.playerscard[source]) == 1:
					self._uno(source, uno, channel)
			else:
				self._notice(source, "You don't have that card.")	
		else:
			self._notice(source, "What did you say?")
	
	# Finds the color and card a player played		
	def _findcard(self, message):
		uno = False
		if message.endswith('uno'):
			uno = True
		if message.startswith('b'):
			color = 'b'
		elif message.startswith('g'):
			color = 'g'
		elif message.startswith('r'):
			color = 'r'
		elif message.startswith('y'):
			color = 'y'
		elif message.startswith('w'):
			color = 'w'
			if 'd' in message:
				card = 'd4'
			else:
				card = ''
			message = ''
		if 'd' in message:
			card = 'd2'
		elif '0' in message:
			card = '0'
		elif '1' in message:
			card = '1'
		elif '2' in message:
			card = '2'
		elif '3' in message:
			card = '3'
		elif '4' in message:
			card = '4'
		elif '5' in message:
			card = '5'
		elif '6' in message:
			card = '6'
		elif '7' in message:
			card = '7'
		elif '8' in message:
			card = '8'
		elif '9' in message:
			card = '9'
		elif 's' in message:
			card = 's'
		elif 'r' in message:
			card = 'r'	
		return (color, card, uno)
	
	# Plays normal cards 0-9		
	def _play(self, source, card, color, channel):
		self.playerscard[source].remove(color + card)
		self.lastcolor = color
		self.lastcard = card
		player = self._player()
		self._message(channel, "%s plays %s to %s" % (source, self.cardvalues[color + card], player))
		self._cardnotice(player)
		self.drew = False
	
	# Plays draw two cards	
	def _drawtwo(self, source, card, color, channel):
		self.playerscard[source].remove(color + card)
		self.lastcolor = color
		self.lastcard = card
		skippedplayer = self._player()
		player = self._player()
		self._dealcard(skippedplayer, 2)
		self._message(channel, "%s plays %s to %s, %s draws two cards and play continues with %s" % (source, self.cardvalues[color + card], skippedplayer, skippedplayer, player))
		self._cardnotice(player)
		self.drew = False
	
	# Plays skip cards
	def _skip(self, source, card, color, channel):
		self.playerscard[source].remove(color + card)
		self.lastcolor = color
		self.lastcard = card
		skippedplayer = self._player()
		player = self._player()
		self._message(channel, "%s plays %s and skips %s to %s" % (source, self.cardvalues[color + card], skippedplayer, player))
		self._cardnotice(player)
		self.drew = False
	
	# Plays reverse cards	
	def _reverse(self, source, card, color, channel):
		self.playerscard[source].remove(color + card)
		self.lastcolor = color
		self.lastcard = card
		self.players.reverse()
		player = self._player()
		self._message(channel, "%s plays %s and play continues with %s" % (source, self.cardvalues[color + card], player))
		self._cardnotice(player)
		self.drew = False
	
	# Plays wild cards then waits for a color choice
	def _wildstart(self, source, card, color, channel):
		self.playerscard[source].remove(color + card)
		self._message(channel, "%s plays %s, pick a color %s" % (source, color + card, source))
		if card == 'd4':
			player = self._player()
			self._dealcard(player, 4)
			self._message(channel, "%s draws four cards and is skipped" % (player))
		self.wilder = source
		self.lastcolor = 'w'
		self.lastcard = ''
		self.wild = True
		self.drew = False
	
	# Sets the color a player chose
	def _wildcontinue(self, source, channel, color):
		if color.startswith('b'):
			self.lastcolor = 'b'
			color = 'Blue'
		elif color.startswith('g'):
			self.lastcolor = 'g'
			color = 'Green'
		elif color.startswith('y'):
			self.lastcolor = 'y'
			color = 'Yellow'
		elif color.startswith('r'):
			self.lastcolor = 'r'
			color = 'Red'
		else:
			self._notice(source, "Please choose a color.")
		if self.lastcolor != 'w':
			player = self._player()
			self._message(channel, "%s chooses %s play continues with %s" % (source, color, player))
			self.wild = False
			self.lastcard = 'UNO'
			self._cardnotice(player)
	
	# Checks if a player with one card said "UNO"
	def _uno(self, player, uno, channel):
		if uno:
			self._message(channel, "%s has UNO!" % (player))
		else:
			self._dealcard(player, 2)
			self._message(channel, "%s draws two cards for not saying UNO!" % (player))
	
	# Draws a card for a player		
	def _draw(self, source, channel):
		if not self.drew:
			self._dealcard(source, 1)
			self.drew = True
			self._message(channel, "%s drew a card." % (source))
		else:
			self._notice(source, "You already drew a card. Please pass or play.")
	
	# Passes the game to the next player if a card was drawn and current player cannot play		
	def _pass(self, source, channel):
		if self.drew:
			self.drew = False
			player = self._player()
			self._message(channel, "%s passes to %s" % (source, player))
			self._cardnotice(player)
		else:
			self._notice(source, "You must draw before you can pass.")
	
	# Function to deal cards		
	def _dealcard(self, source, deal):
		cards = []
		if len(self.cards) - deal < 1:
			self.cards = self.globcards
			random.shuffle(self.cards)
		while deal:
			card = self.cards.pop()
			self.playerscard[source].append(card)
			cards.append(self.cardvalues[card])
			deal = deal - 1
		self._notice(source, ', '.join(cards))
	
	# Notices a player with the cards they have		
	def _cardnotice(self, player):
		cards = []
		for item in self.playerscard[player]:
			cards.append(self.cardvalues[item])
		self._notice(player, ', '.join(cards))
	
	# Sends a notice
	def _notice(self, source, message):
		if source != 'ai':
			if not self.stop:
				self.msgvars["server"].notice(source, message)
	
	# Sends a message
	def _message(self, channel, message):
		if not self.stop:
			self.msgvars["server"].privmsg(channel, message)
	
	# Rotates the turns and returns the current player
	def _player(self):
		if self.currentplayer + 1 < len(self.players):
			self.currentplayer = self.currentplayer + 1
		else:
			self.currentplayer = 0
		return self.players[self.currentplayer]
	
	# Signals a game has been won	
	def _win(self, channel, source):
		del self.globalvars["localsometimes"][self.msgvars["chanid"]]
		self.playerscard[source] = []
		self.stop = True
	
	# Stops the game and clears the instance	
	def _stop(self, source, channel):
		del self.globalvars["localsometimes"][self.msgvars["chanid"]]
		self._message(channel, "%s stopped the game" % (source))
		
def autoload(globalvars):
	
	# Loads oncall
	oncall = {'uno':uno}
	for feature in oncall.items():
		if feature[0] not in globalvars["oncall"].keys():
			globalvars["oncall"][feature[0]] = feature[1]
	
def load(globalvars, msgvars):
	
	# Loads oncall
	oncall = {'uno':uno}
	for feature in oncall.items():
		if feature[0] not in globalvars["oncall"].keys():
			globalvars["oncall"][feature[0]] = feature[1]
		else:
			msgvars["server"].privmsg(msgvars["destination"], "There is already a function named %s." % (feature[0]))
	return True
			
def unload(globalvars, msgvars, reloadmodule = False):
	
	# Unloads oncall
	oncall = {'uno':uno}
	for feature in oncall.items():
		if feature[0] in globalvars["oncall"].keys():
			del globalvars["oncall"][feature[0]]
	return True
